//***************************************************************************//
//*
//* 内容摘要：单个传输通道封装类，封装FEC\QOS\RTP过程
//*	
//* 当前版本：V1.0		
//* 作    者：zhfayuan
//* 完成日期：2016-8-21
//**************************************************************************//

#if !defined(SDRTPCOMM_H)
#define SDRTPCOMM_H

#include "SDCommon.h"
#include "SDThread.h"
#include "SDFecDecProcess.h"
#include "SDFecEncProcess.h"
#include "SDQos.h"
#include "SDRtp.h"



#define   UDP_PACK_DATA_BLOCK               0x9AC97DFD
#define   UDP_PACK_HANDLE_BLOCK             0xDAC97CFE

#define   RECIVE_BLOCK_SIZE_UDP                1000
//收到对端IDR请求后，本接口将被调用，外层可以在其中响应IDR请求（注意本接口为同步调用，请勿在其中进行耗时操作）
typedef void (*CallBack_OnReciveIdrReq)(void* pObject);

//本端发送IDR请求接口
typedef void (*CallBack_OnSendIdrReq)(void* pObject);

typedef struct
{
	//HEAD
	int      ncrc;
	UINT     unHeadFlags;
	UINT     nLen;
	//用于CRC计算的随机数
	UINT	 nRandomNum;

}NET_BLOCK_UDP_HEAD;

typedef struct
{
	//HEAD
	NET_BLOCK_UDP_HEAD head;
	//DATA
	BYTE     blockbuf[RECIVE_BLOCK_SIZE_UDP];
}NET_BLOCK_UDP;


typedef struct  
{
	UINT     unLostRatio;  //用来反馈丢包率（乘100后）
	//本地发送时间
	timevalCommon tLocalSendTime;
	//本地接收时间
	timevalCommon tLocalRecvTime;
	//远端发送时间
	timevalCommon tRemoteSendTime;

}DATA_FOR_NET_STATUS_CALC;



#define   JW_INT(data)   ((data / sizeof(int) + !!(data % sizeof(int))) * sizeof(int))

/*
 *  说明：此类为一纯虚类即接口类，功能的实现需要子类完成。提供给udp通讯类向上层通告数据包到来
 */
class CSDCommReciveProcess
{
public:
	CSDCommReciveProcess()
	{}

	virtual ~CSDCommReciveProcess()
	{}

public:
	virtual void OnReciveData(int nLen, BYTE *byBuf) = 0;
	virtual void OnReciveError() = 0;
};

class CSDNeedRemoteRetransRequestBase
{
public:
	CSDNeedRemoteRetransRequestBase()
	{}

	virtual ~CSDNeedRemoteRetransRequestBase()
	{}

public:
	virtual void NeedRemoteReTransRequest(unsigned short usStartSeq, unsigned short usEndSeq)
	{

	}

	//NACK需要获取更为准确的RTT，在关闭FEC\QOS功能的NACK REQ通道上获取更加适合
	//对于开启了FEC\QOS的通道，若无媒体数据传输时，RTT精度将受丢包影响
	virtual UINT GetCurrentTransRtt()
	{
		return 0;
	}

	//对方是否开启NACK功能
	virtual BOOL GetRemoteNackStatus()
	{
		return FALSE;
	}
};

struct T_PacketBuffer;
struct T_PacketBufMan;

/******************************************************************************
 * 类名称  : CSDRTPBase
 * 功能描述  : 基于RTP,FEC,QOS的基类传输，其发送大小不能超过m_nMaxBlockSize大小
               ，接收数据是通过CSDCommReciveProcess类来通知上层应用
 * 修改记录  : 
 * 修改日期        版本号        修改人        修改内容   
 * -----------------------------------------------------------------------------
 ******************************************************************************/
class CSDRTPBase : public CSDFecEncProcess, public CSDFecDecProcess, public CSDQos, public CSDRtp
{
public:
	CSDRTPBase(NET_TRANS_TYPE type, UINT unLocalSaveRtpPacketsNum);
	virtual ~CSDRTPBase();

public:

	//创建基本rtp通讯对象
	BOOL RTPBaseCreate(CSDCommReciveProcess *pReciveProc, CSDNeedRemoteRetransRequestBase* pNackReq, 
						mysockaddr tLocalAddr, mysockaddr tRemoteAddr, const char* strName = NULL);

	//释放基本rtp通讯对象
	void RTPBaseClose();

	//向远端发送数据
	BOOL RTPBaseSend(int nLen, BYTE *byBuf, NET_TRANS_TYPE type, UINT unNaluNum, UINT unNaluIndex);

	//设置发送FEC冗余度百分比
	BOOL SetTransQuality(int nValue);

	//设置FEC GROUP单元包含的媒体包数
	BOOL SetFecGroupSize(int nValue);

	//设置用于接收RTP数据包的BUFF缓冲池深度，对于服务器线路需要较大的深度避免包耗尽
	void SetBuffManDepth(int nBuffDepth);

	//是否打开发送端FEC，不调用本接口时默认为打开
	void SetFecEncodeEnable(bool bEnable = true);

	//获得该链路上统计周期内的发送码率(kbps，包括IP头、UDP头、RTP头)
	float MDrtpGetUpBitrate() {return m_fSendBitrate;}

	//获得远端IP和端口（从收到远端的包中提取得到，为NAT后IP和端口）
	void GetRemoteNatIpAndPort(UINT &unIp, USHORT &usPort);

private:
	//最大的传输块大小
	volatile USHORT        m_nMaxBlockSize;
	//RTP负载类型
	NET_TRANS_TYPE         m_type;
	//本地端口
	USHORT                 m_shLocalPort;
	//用于QOS排序的RTP包序号
	USHORT                 m_shSeqNum;
	//远端地址
	mysockaddr             m_tRemoteAddr;
	//输出回调接口
	CSDCommReciveProcess*  m_pReciveProcess;
	//NACK请求回调接口
	CSDNeedRemoteRetransRequestBase* m_pNackReq;
	//网络接收线程
	CSDThread              *m_pRecThed;

	//存放待发送数据的Buf
	T_PacketBuffer*        m_pSendPacketBuffer;
	T_PacketBufMan*        m_pRecivePacketBufMan;
	void*                  m_csRecivePacketBufMan;
	void*                  m_csSend;
	CSDEvent               m_evtRecv;

	unsigned char          m_ucExt[FEC_RTP_EXHDR_LEN];
	int					   m_nBuffDepth;

	//模块已经接收或者发送的包数
	UINT				   m_unRecvPacketNum;
	UINT				   m_unSendPacketNum;

	//用于统计发送码率
	unsigned int		   m_unPrevStatisTimestamp;
	//统计周期内的发送字节数
	unsigned int		   m_unSendBytesInWindow;
	//统计周期内的码率情况
	float				   m_fSendBitrate;							

	//模块是否Close
	BOOL				   m_bClosed;	
	char				   m_acName[128];
	static char			   m_strTag[];

	//收到的RTP头部长度
	UINT				   m_unRtpHeadSize;

protected:
	virtual BOOL InsertReciveData(int nLen, BYTE* byBuf);

private:
	//RTP接收线程
	static int	ThreadRecvProc(void* pParam);
	static int	ThreadRecvClose(void* pParam);

	BOOL		mfReciveData();
	void		mfClose();
	MD_STATUS	mfRtpSendPacket(T_PacketBuffer *pPacketBuffer, T_PacketBuffer *pOutPacketBuffer, T_rtpParam *p);	
	int			mfRtpRecvAndQosFecOut(struct sockaddr *from, int *fromlen, UINT unTimeOut = 0);
	MD_STATUS	mfRtpRecvPacket(T_PacketBuffer *pPacketBuffer, T_rtpParam *p, struct sockaddr *from, int *fromlen, UINT unTimeOut);	

	//FEC编码器类所需实现的虚函数
	MD_STATUS	pfFecOutCallback(T_PacketBuffer *pInPacketBuffer, T_PacketBuffer *pOutPacketBuffer); 

	//FEC解码的虚函数实现
	MD_STATUS	pfFecDecOutCallback(T_PacketBuffer *pOutPacketBuffer);
	MD_STATUS	pfFecEncOutCallback(T_PacketBuffer *pInPacketBuffer, T_PacketBuffer *pOutPacketBuffer);
#if ENABLE_NACK
	MD_STATUS	pfNackPacketOutCallback(UINT8 *pBuffer, UINT32 unLen);
#endif
	T_PacketBuffer *pfGet2KPacketBufCallback(void);
	
	//QOS的虚函数实现
	void		pfQosOutCallback(T_PacketBuffer *pPacketBuffer);
	//当发现序号不连续时（可能为乱序或者丢包)，并不是所有情况下都需要等待一定时间（最大等待uLostDelay）
	//在FEC配合使用时，若该group的媒体包未丢失或已恢复，则允许多余冗余包丢失，此时将不予等待，避免引入不必要的抖动延时。
	BOOL		pfNeedWaitForLostPacketCallback(T_PacketBuffer *pPacketBuffer);

#if ENABLE_NACK
	//检测当前丢包是否将导致FEC无法恢复，作为NACK发起的依据
	BOOL		pfNeedReTransPacketCallback(T_PacketBuffer *pPacketBuffer);

	//请求NACK远端重传指定序号的数据包
	void		pfNeedRemoteReTransRequestCallback(unsigned short usStartSeq, unsigned short usEndSeq);

	//获取当前RTT时间，用于评估合适的NACK等待时间
	UINT		pfGetCurrentTransRttExtern();

	//对端是否使能NACK，只有对端使能的情况下本地NACK才有意义
	BOOL		pfRemoteNackIsEnable();
#endif
};

/******************************************************************************
 * 类名称  : CSDRTPSliceCommn
 * 功能描述  : 此类继承CSDRTPBase所有特性，加入收发数据的CRC校验功能，避免非法数据攻击
               接收数据同样通过CSDCommReciveProcess类来通知上层应用
			   发送时，提供媒体包和握手包两种数据包类型的对应接口
 * 修改记录  : 
 * 修改日期        版本号        修改人        修改内容   
 * -----------------------------------------------------------------------------
 ******************************************************************************/
class CSDRTPSliceCommn : public CSDRTPBase
{
public:
	CSDRTPSliceCommn(NET_TRANS_TYPE type, UINT unLocalSaveRtpPacketsNum = 0);
	virtual ~CSDRTPSliceCommn();

public:

	BOOL RTPSliceCommnCreate(CSDCommReciveProcess *pReciveProc, CSDNeedRemoteRetransRequestBase* pNackReq, 
							mysockaddr tLocalAddr, mysockaddr tRemoteAddr, const char* strName = NULL);
	void RTPSliceCommnClose();
	BOOL RTPSliceCommnMediaPacketSend(int nLen, BYTE *byBuf, UINT unNaluNum = 0, UINT unNaluIndex = 0);
	BOOL RTPSliceCommnHandlePacketSend();

	//是否开启FEC上行冗余自适应
	void DynamicRedunEnable(bool bEnable = false);
	//从心跳包中获得上行的丢包率数据
	UINT GetUpLostRatio() { return m_unUpLostRatio; } 
	//从心跳包中获得网络延时（从本端到远端再回到本端的时间）
	UINT GetNetworkDelayInMs() { return m_tCurrDelay.tv_sec * 1000 + m_tCurrDelay.tv_usec / 1000; }
	//是否接收到远端的心跳包
	BOOL HaveRecvHandMsg() { return m_bHaveRecvHandMsg; }


#if ENABLE_NACK
	//设置处理远端IDR请求的回调函数
	void SetRemoteIdrReqProcesser(CallBack_OnReciveIdrReq pfIdrReqProcess, void* pIdrReqObject);
	//收到远端IDR帧请求，告知外层，底层虚函数的实现
	void pfRemoteRequestIdrCallback();


	//设置本端向远端发送IDR请求的回调函数
	void SetRemoteIdrSendProcesser(CallBack_OnSendIdrReq pfIdrSendProcess, void* pIdrSendObject);
	//本端接收丢包且无法恢复，通过NACK信令通道向远端请求IDR
	void pfNeedRemoteSendIdrCallback();
#endif
protected:
	virtual BOOL InsertReciveData(int nLen, BYTE* byBuf);

private:

	NET_TRANS_TYPE	m_type;
	char		    m_acName[128];
	bool			m_bEnableDynamicRedun;
	UINT			m_unPrevRedundancy;
	UINT			m_unUpLostRatio;
	//是否收到过握手包; 客户端一开始会向服务器发送握手包，服务器收到握手包后即知道了客户端NAT过后的IP、端口。
	//使用该IP端口，服务器便能将数据(包括有效数据和握手包)发给客户端。如果客户端登录后长时间未收到过握手包
	//则极有可能是其UDP未能成功穿越(影响因素比较多，如防火墙端口限制等)，此时上层应该处理这种情况，否则用户将
	//一直收不到音视频
	BOOL			m_bHaveRecvHandMsg;  

	//用于统计网络线路时延(双方链路的握手包中携带本信息，用于统计数据包从本地发出、对方接收、对方发出、本地接收的整体时长)
	DATA_FOR_NET_STATUS_CALC m_dataForStatusCalc;
	timevalCommon	m_tCurrDelay;

#if ENABLE_NACK
	CallBack_OnReciveIdrReq	m_pfIdrReqProcess;
	void*					m_pIdrReqObject;
	CallBack_OnSendIdrReq   m_pfIdrSendProcess;
	void*					m_pIdrSendObject;
#endif
};


#endif // !defined(SDRTPCOMM_H)
